#include "jobmanager.h"
#include "jobthread.h"
#include "job.h"
#include "util/assert.h"

namespace gecko
{

	JobManager::JobManager( unsigned int numJobThreads )
	{
#if defined(NO_MULTITHREADING)
		numJobThreads = 0;
		return;
#else
		for ( unsigned int i = 0; i < numJobThreads; ++i )
		{
			JobThread * thread = new JobThread(this);
			m_idleThreads.PushBack(thread);

			thread->Initialise("JobThread");
		}
#endif
	}

	void JobManager::AddJobToQueue(Job * job)
	{
		m_jobQueue.Enqueue(job);
	}

	Job * JobManager::GetJob()
	{
		JobQueue::Node * jobNode = m_jobQueue.Dequeue();

		Job * job = 0;

		if (jobNode)
		{
			job = jobNode->Data;
			delete jobNode;
		}

		return job;
	}

	void JobManager::ProcessJobs()
	{
#if defined(NO_MULTITHREADING)
		while (Job * job = GetJob())
		{
			job->Process();
		}
#else
		for ( unsigned int i = 0; i < m_idleThreads.Size(); ++i )
		{
			DistributeJob(NULL);
		}

		// wait for jobs to finish
		m_jobWaitEvent.Wait();
#endif
	}

	void JobManager::DistributeJob( JobThread * waitingThread )
	{
#if defined(NO_MULTITHREADING)
		waitingThread = NULL;
		return;
#else
		Job * job = GetJob();

		if ( waitingThread )
		{
			if ( job )
			{
				waitingThread->SetCurrentJob(job);
			}
			else
			{
				// no tasks left, suspend thread
				m_runningThreads.Remove(waitingThread);
				m_idleThreads.PushBack(waitingThread);

				if (m_runningThreads.Empty())
				{
					m_jobWaitEvent.Signal();
				}
			}
		}
		else
		{
			if ( job && !m_idleThreads.Empty() )
			{
				// have we got any idle threads?
				JobThread * thread = m_idleThreads.Front();
				m_idleThreads.PopFront();
				m_runningThreads.PushBack(thread);

				thread->SetCurrentJob(job);
			}
		}
#endif
	}

	bool JobManager::Destroy()
	{
#if defined(NO_MULTITHREADING)
		return true;
#else

		DebugPrint("Closing thread pool threads");

		for ( ThreadList::Iterator itr = m_runningThreads.Begin(); itr != m_runningThreads.End(); ++itr )
		{
			JobThread * thread = *(itr);
			thread->KillProcessing();

			// wait for thread to end
			while (thread->IsRunning())
			{
				ThreadSleep(1);
			}

			delete thread;
		}

		for ( ThreadList::Iterator itr = m_idleThreads.Begin(); itr != m_idleThreads.End(); ++itr )
		{
			JobThread * thread = *(itr);
			thread->KillProcessing();

			// wait for thread to end
			while (thread->IsRunning())
			{
				ThreadSleep(1);
			}

			delete thread;
		}

		m_jobQueue.Clear();
		m_idleThreads.Clear();
		m_runningThreads.Clear();

		DebugPrint("Threads destroyed");

		return true;
#endif
	}

}
